#include "OgreApplication.h"

SoundInstance::SoundInstance(){
	this->initNull();

}

SoundInstance::SoundInstance(Ogre::String name,Ogre::String resGroupName,SoundType type){
	this->initNull();

	std::cout << "Init Sound: "<< name << "\n";

	this->fileName			= name;
	this->resourceGroupName	= resGroupName;
	this->sound_type		= type;
}

void SoundInstance::initNull(){

	this->channel				= -1;
	this->effect				= NULL;
	this->fileName				= "";
	this->fmod_sound			= NULL;
	this->resourceGroupName		= "";
	this->sound_type			= INVALID_SOUND;
	this->timeToDelete			= 0;
	this->strPtr.setNull();

}

SoundInstance::~SoundInstance(){
	std::cout << "Destroy Sound: "<< this->getName() << "\n";
	this->destroyEffects();
	this->destroyFMODSound();
	this->destroyStreampointer();
}

void SoundInstance::destroyEffects(){

	if(this->effect == NULL)return;

	if(this->effect->isStandard)return;

	delete this->effect;
	this->effect = NULL;

}

void SoundInstance::destroyFMODSound(){

	if(this->fmod_sound == NULL)return;

	this->fmod_sound->release();

	delete this->fmod_sound;
}

void SoundInstance::destroyStreampointer(){
	if(this->strPtr.isNull())return;

	this->strPtr->close();

	this->strPtr.setNull();
}

//methods

void SoundInstance::setChannel(int channel){
	this->channel = channel;
}

int SoundInstance::getChannel(){
	return this->channel;
}

SoundType SoundInstance::getSoundType(){
	return this->sound_type;
}

void SoundInstance::setSoundType(SoundType type){
	if(this->sound_type != INVALID_SOUND)return;
	this->sound_type = type;
}

bool SoundInstance::isLooped(){
	return (this->sound_type == LOOPED_STREAM_SOUND || this->sound_type == LOOPED_ROOM_SOUND);
}

bool SoundInstance::isRoom(){
	return (this->sound_type == LOOPED_ROOM_SOUND || this->sound_type == ROOM_SOUND);
}

bool SoundInstance::isScreen(){
	return (this->sound_type == LOOPED_STREAM_SOUND || this->sound_type == STREAM_SOUND);
}

void SoundInstance::create(float distance_to_player){

	this->timeToDelete = distance_to_player/SoundHandler::ogre_init_setts->SPEED_OF_SOUND;

}

void SoundInstance::updateTimer(unsigned long millis){
	this->timeToDelete -= millis;
}

bool SoundInstance::isReadyToPlay(){
	return(this->channel == -1 && this->timeToDelete <= 0);
}

void SoundInstance::play(){
	if(this->fmod_sound == NULL)return;
	
	unsigned int help = -1;

	this->fmod_sound->getLength(&help,FMOD_TIMEUNIT_MS);
	
	this->timeToDelete = (int) help;

	if(this->isLooped())this->timeToDelete = 2147483647;

	std::cout << "PlaySound "<<this->fileName << " length: "<< this->timeToDelete << "\n";
}

void SoundInstance::stop(){
	this->timeToDelete = 0;
}

void SoundInstance::stop(unsigned long millis){
	this->timeToDelete = millis;
}

bool SoundInstance::isReadyToDelete(){
	if(this->timeToDelete <= 0)return true;
	return false;
}

void SoundInstance::setEffect(SoundEffects *effect){
	this->destroyEffects();

	this->effect = effect;
}

SoundEffects* SoundInstance::getEffect(){
	return this->effect;
}

void SoundInstance::setFMODSound(FMOD::Sound* sound){
	this->destroyFMODSound();
	this->fmod_sound = sound;
}

FMOD::Sound* SoundInstance::getFMODSound(){
	return this->fmod_sound;
}

void SoundInstance::setStreamPointer(Ogre::DataStreamPtr ptr){
	this->destroyStreampointer();
	this->strPtr = ptr;
}

Ogre::DataStreamPtr SoundInstance::getStreamPointer(){
	return this->strPtr;
}

void SoundInstance::setResGroupNName(Ogre::String name, Ogre::String resGroupName){
	this->fileName			= name;
	this->resourceGroupName	= resGroupName;
}

Ogre::String SoundInstance::getName(){
	return this->fileName;
}

Ogre::String SoundInstance::getResGroupName(){
	return this->resourceGroupName;
}

Ogre::Archive* SoundInstance::getFullPath(){

	if(! SoundHandler::ogre_app->getResourceGroupManager()->isResourceGroupLoaded(this->resourceGroupName))return NULL;

	Ogre::FileInfoListPtr list = SoundHandler::ogre_app->getResourceGroupManager()->findResourceFileInfo(this->resourceGroupName,this->fileName);

	if(list->size() > 0)return list->at(0).archive;

	return NULL;
}






//ScreenSound-----------------------------------------------------------------------------------------------------------------------------

ScreenSound::ScreenSound(){
}

ScreenSound::~ScreenSound(){
}

//SoundNode-------------------------------------------------------------------------------------------------------------------------------

SoundNode::SoundNode(){
	this->initNull();
	this->initNullNodes();
	
}

SoundNode::SoundNode(Ogre::String name,Ogre::String resGroupName,bool looped,RootSoundNode *parent, Ogre::Vector3 position ){
	this->initNull();

	
	this->setResGroupNName(name,resGroupName);
	if(looped){
		this->setSoundType(LOOPED_ROOM_SOUND);
	}
	else{
		this->setSoundType(ROOM_SOUND);
	}
	
	
	this->initNullNodes();
	this->parent = parent;
	this->initNode(position);
}

void SoundNode::initNullNodes(){
	this->parent = NULL;
	this->node	 = NULL;
}

void SoundNode::initNode(Ogre::Vector3 position){
	this->destroyNode();
	if(this->parent == NULL)return;
	
	this->node = this->parent->getMainNode()->createChildSceneNode();
	
	this->node->translate(position);

}

SoundNode::~SoundNode(){
	this->destroyNode();
}

void SoundNode::destroyNode(){

	if(this->node == NULL)return;
	
	if(this->parent == NULL)SoundHandler::ogre_app->getSceneManager()->destroySceneNode(this->node);
	if(this->parent != NULL){
	this->parent->getMainNode()->removeAndDestroyChild(this->node->getName());
	this->parent->remove(this);
	}
	this->node = NULL;

}

void SoundNode::setSceneNode(Ogre::SceneNode *node){
	this->destroyNode();
	this->node = node;
}

Ogre::SceneNode* SoundNode::getSceneNode(){
	return this->node;
}

Ogre::Vector3 SoundNode::getPosition(){
	if(this->node == NULL)return Ogre::Vector3(0,0,0);
	
	Ogre::Vector3 offset = this->node->_getDerivedPosition(); 

	if(this->parent == NULL)return offset*SoundHandler::ogre_init_setts->METER;
	
	return offset*SoundHandler::ogre_init_setts->METER + this->parent->getRealPlayPos();
}

RootSoundNode* SoundNode::getParent(){
	return this->parent;
}

void SoundNode::setParent(RootSoundNode *node){
	this->parent = node;
}

//RootSoundNode-------------------------------------------------------------------------------------------------------------------------------

RootSoundNode::RootSoundNode(){
	this->initNull();
	this->initializeVector();
}

RootSoundNode::RootSoundNode(RootSoundDeleteCondition state,Ogre::Vector3 position,Ogre::SceneNode* attached,SoundEffects* effect){
	this->initNull();
	this->initializeVector();
	this->initializeNode(position,attached);
	
	this->deleteCondition = state;
	this->effects = effect;
}

void RootSoundNode::initNull(){

	std::cout << "Init RootSoundNode!\n";
	this->deleteCondition		= INVALID_CONDITION;
	this->subSoundVector		= NULL;
	this->main_node				= NULL;
	this->previousPosition		= Ogre::Vector3(0,0,0);
	this->effects				= NULL;
	this->static_sound			= false;
	this->ss_type				= S_PLANE_BOOM;
	this->distance_player		= 1.0f;
	this->realPlayPosition		= Ogre::Vector3(0,0,0);
	this->velVector				= Ogre::Vector3(0,0,0);
	this->isSuperS				= false;
	this->player_ss_muted		= false;

}

void RootSoundNode::initializeNode(Ogre::Vector3 position,Ogre::SceneNode* attached){
	this->destroyNode();

	this->main_node = SoundHandler::ogre_app->getSceneManager()->createSceneNode();
	
	if(attached == NULL){
		SoundHandler::ogre_app->getSceneManager()->getRootSceneNode()->addChild(this->main_node);
		this->static_sound	= true;
		this->previousPosition = position;
	}
	else{
		attached->addChild(this->main_node);
		this->previousPosition = this->main_node->_getDerivedPosition();
	}

	this->main_node->setPosition(position);
}

void RootSoundNode::initializeVector(){

	if(this->subSoundVector != NULL)return;
	
	this->subSoundVector	= new SoundNodeVector(); 

}

RootSoundNode::~RootSoundNode(){
	std::cout << "Deleting RootSoundNode!\n";
	this->destroySoundEffects();
	this->destroyVector();
	this->destroyNode();
	
}

void RootSoundNode::destroySoundEffects(){

	if(this->effects == NULL)return;
	
	if(this->effects->isStandard)return;
	
	delete this->effects;
}

void RootSoundNode::destroyVector(){
	if(this->subSoundVector == NULL)return;

	for(unsigned int i = 0; i < this->subSoundVector->size();i++){ 
		this->subSoundVector->at(i)->stop();
		this->subSoundVector->at(i)->setParent(NULL);
	}
	
	this->subSoundVector->clear();
	delete this->subSoundVector;
	this->subSoundVector = NULL;
}

void RootSoundNode::destroyNode(){
	if(this->main_node == NULL)return;

	this->main_node->getParentSceneNode()->removeAndDestroyChild(this->main_node->getName());

}


void RootSoundNode::stopAllSounds(unsigned long millis){

	for(unsigned int i = 0; i < this->subSoundVector->size();i++){
		this->subSoundVector->at(i)->stop(millis);	
	}
	
}


//get / sets

void RootSoundNode::setDistance(float dist){
	this->distance_player = dist;
}

float RootSoundNode::getDistance(){
	if(this->distance_player <= 0)return 1;
	return this->distance_player;
}

Ogre::Vector3 RootSoundNode::getRealPlayPos(){
	//return this->main_node->_getDerivedPosition();
	return this->realPlayPosition;
}

void RootSoundNode::setMainNode(Ogre::SceneNode *node){
	this->destroyNode();
	this->main_node = node;
}

Ogre::SceneNode* RootSoundNode::getMainNode(){
	return this->main_node;
}

Ogre::Vector3 RootSoundNode::getVelocityVector(){
	return this->velVector;
}

Ogre::Vector3 RootSoundNode::calculateVelVector(unsigned long millis){
	Ogre::Vector3 vel = this->main_node->_getDerivedPosition()-(this->previousPosition);
	vel = vel* 1000 * SoundHandler::ogre_init_setts->METER / (float) millis;
	return vel;
}

SoundEffects* RootSoundNode::getEffect(){
	return this->effects;
}

bool RootSoundNode::isStatic(){
	return this->static_sound;
}

bool RootSoundNode::isPlayerSSMuted(){
	return this->player_ss_muted;
}

void RootSoundNode::resetPlayerSSMute(){
	this->player_ss_muted = false;
}

void RootSoundNode::deleteManual(){
	this->deleteCondition = DELETE_NOW_CONDITION;
}

bool RootSoundNode::isReadyToDelete(float distance_to_player){
	
	
	switch(this->deleteCondition){

		case INVALID_CONDITION:		return true;break;
		case DELETE_NOW_CONDITION:	return true;break;
		case MANUAL_DELETE_CONDITION: return false;break;

		case NO_SUBSOUND_DELETE_CONDITION:

			if(this->subSoundVector->size() == 0)return true;
			return false;break;

		case DISTANCE_DELETE_CONDITION:

			if(this->effects == NULL)return true;

			if(this->effects->max_distance < distance_to_player)return true;
			return false;break;
	}
	return true;
}

void RootSoundNode::updateTimesAndVelocitys(Ogre::Vector3 player_pos, float distance_to_player, unsigned long millis){


	if(this->isStatic() || this->player_ss_muted)return;
	this->velVector = this->calculateVelVector(millis);
	this->realPlayPosition = this->calculateRealPlayPos(distance_to_player,this->getVelocityVector());
	
	bool curSuper = this->calculateSuperSonic(player_pos);

	this->isSuperS = curSuper;

	this->previousPosition = this->main_node->_getDerivedPosition();
}

Ogre::Vector3 RootSoundNode::calculateRealPlayPos(float distance_to_player,Ogre::Vector3 velVector){
	Ogre::Vector3 ret = this->main_node->_getDerivedPosition()*SoundHandler::ogre_init_setts->METER;
	if(this->isStatic())return ret;
	
	float time = distance_to_player/SoundHandler::ogre_init_setts->SPEED_OF_SOUND;
	
	ret = ret + velVector*-1*time; 

	return ret;
}


void RootSoundNode::add(SoundNode* node){
	if(node == NULL)return;

	this->subSoundVector->push_back(node);

}

void RootSoundNode::calcPlayerSSMuted(Ogre::Plane player_plane,Ogre::Vector3 player_vel_vec){

	
	Ogre::Ray player_ray (this->main_node->_getDerivedPosition(),player_vel_vec);
	
	this->player_ss_muted = player_ray.intersects(player_plane).first;

}

bool RootSoundNode::calculateSuperSonic(Ogre::Vector3 player_pos){
	
	if(this->isStatic() || this->velVector.isZeroLength())return false;
	
		Ogre::Vector3 vel = this->velVector;
	
		if(vel.squaredLength() < SoundHandler::ogre_init_setts->SQUARED_SPEED_OF_SOUND)return false;
	
	
		Ogre::Vector3 per = vel.perpendicular();
		
		Ogre::Plane plane_curr (this->main_node->_getDerivedPosition(),per,vel.crossProduct(per));
		
		
		Ogre::Ray player_ray (player_pos,-1*vel);
		
		bool inter1 = player_ray.intersects(plane_curr).first;

		if(! inter1 && this->isSuperS)SoundHandler::ogre_app->getSoundHandler()->playSonicBoom(this->getRealPlayPos(),this->ss_type);

	return inter1;
	
	
}

bool RootSoundNode::isSuperSonic(){
	return this->isSuperS;
}

void RootSoundNode::remove(SoundNode* node){
	if(node == NULL)return;
	
	int size = this->subSoundVector->size();

	for(int i = 0; i < size;i++){
		
		if(node == this->subSoundVector->at(i)){
			this->subSoundVector->erase(this->subSoundVector->begin()+i);
			size--;i--;
		}
		
	}

}